<?php


namespace App\Exception\Handler;


use App\Exception\HttpException;
use App\Lib\Tools\Log;
use Hyperf\Di\Annotation\Inject;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Throwable;

class HttpExceptionHandler  extends ExceptionHandler
{
    /**
     * @Inject()
     * @var RequestInterface
     */
    protected $request;

    protected $httpStatusCode= 500;
    protected $message = "系统内部错误,请联系管理员";


    protected $logger;
    /**
     * @Inject()
     * @var \Hyperf\Contract\ConfigInterface
     */
    protected $config;

    public function handle(Throwable $except, ResponseInterface $response)
    {
        $this->stopPropagation();
//        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
//        $this->logger->error($throwable->getTraceAsString());
//        return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
        if($except instanceof HttpException){
            //已知、自定义异常处理
            $this->httpStatusCode = $except->httpStatusCode;
            $message = $except->getMessage() ?: $this->config->get("except_type.{$except->code}") ?: '未定义错误信息';
            if(is_string($message) && strpos($message,'$') !== false){
                $message  = $this->messageConvert($message);
            }
            if(is_array($message)){
                foreach ($message as &$msgItem){
                    $msgItem = $msgItem->getMessage();
                }
            }
            $data = [
                'code'  => $except->code,
                'message' => $message,
                'request_url' => $this->request->getRequestUri()
            ];
        }elseif ($except instanceof ValidationException){
            $body = $except->validator->errors()->first();
            $data = [
                'code'  => 10030,
                'message' => $body ,
                'request_url' => $this->request->getRequestUri()
            ];
        }elseif($except instanceof \Hyperf\HttpMessage\Exception\HttpException){
            // 被框架CoreMiddleWare处理过后捕获的HttpException行为
                $this->httpStatusCode = $except->statusCode;
                $data = [
                    'code'  => $except->statusCode,
                    'message' => $except->getMessage(),
                    'request_url' => $this->request->getRequestUri()
                ];
        }else{
            if (env('APP_ENV')!='dev') {
                if($except->getCode()){
                    $this->httpStatusCode = $except->getCode();
                }else
                $data = [
                    'code'  => 10020,
                    'message' => $this->message,
                    'request_url' =>  $this->request->getRequestUri()
                ];
                $this->recordErrorLog($except);
            }else{
                $data = [
                    'code'  => $except->getCode(),
                    'error' => sprintf('(%s) %s', get_class($except), $except->getMessage()),
                    'file'  => sprintf('At %s line %d', $except->getFile(), $except->getLine()),
                    'trace' => $except->getTraceAsString(),
                ];
            }
        }
//        多层异常嵌套停止冒泡
//        $this->stopPropagation();
        return $response->withHeader('content-type','application/json')
            ->withStatus($this->httpStatusCode)
            ->withBody(new SwooleStream(json_encode($data)));
    }

    public function isValid(Throwable $throwable): bool
    {
        return  true;
//        return $throwable instanceof  HttpException;
    }

    private function recordErrorLog($e){
        $uri = $this->request->getRequestUri();
        $queryString = $this->request->getQueryString();
        $bodyParams = $this->request->getParsedBody();
        $errorMessage = 'uri:'.$uri.PHP_EOL.
            'query_string:'.$queryString.PHP_EOL.
            'body_params:'.json_encode($bodyParams).PHP_EOL.
            'error_message:'.$e->getMessage();
        Log::init('cms',$errorMessage);
    }

    private function messageConvert($message){
        $pattern = '/(?<=\{)[^}]*(?=})/';
        preg_match($pattern,$message,$match);
        if($match[0] && $matchConfigStr = $this->config->get($match[0])){
            $message = str_replace('${'.$match[0].'}',$matchConfigStr,$message);
        }
        return $message;
    }
}